import template from 'art-template'
import * as path from 'path'
import * as mvn from '@main/cmd/mvn'
import * as futils from '@main/utils/file-utils'
import * as cfg from '@main/truck/config'
import plimit from 'p-limit'
import {uploadOptions} from '@/common/default'
import logger from '../utils/logger'
const { XMLParser } = require('fast-xml-parser')

const findGroupId = (project) => {
  return project.groupId || project.parent.groupId
}

const findArtifactId = (project) => {
  return project.artifactId || project.parent.artifactId
}

const findVersion = (project) => {
  return project.version || project.parent.version
}

const findPackageInfo = (project) => {
  return {
    groupId: findGroupId(project),
    artifactId: findArtifactId(project),
    version: findVersion(project)
  }
}

const parsePom = async (pomFilePath) => {
  const xmlData = await futils.readFileContent(pomFilePath)
  const parser = new XMLParser()
  const { project } = parser.parse(xmlData)
  return findPackageInfo(project)
}

const findPomFile = (files) => {
  const pomFiles = files.filter((file) => path.extname(file) === `.pom`)
  return pomFiles.length > 0 ? pomFiles[0] : null
}

const findJarFile = (files) => {
  const jarFiles = files.filter((file) => path.extname(file) === `.jar`)
  return jarFiles.length > 0 ? jarFiles[0] : null
}

const isErrorLogText = (logText) => {
  return logText.indexOf('[INFO] BUILD FAILURE')
}

const packageLogContent = (packageInfo) =>
  `groupId=${packageInfo.groupId} artifactId=${packageInfo.artifactId} version=${packageInfo.version}`

/**
 * 递归查找依赖包信息
 * @param {*} parentPath 上级路径
 * @param {*} packages 包列表
 */
const recursionfindPackageInfo = async (parentPath, packages) => {
  const subFiles = await futils.findSubFiles(parentPath)
  const pomFile = findPomFile(subFiles)
  if (pomFile) {
    const pomFilePath = path.join(parentPath, pomFile)
    const packageInfo = await parsePom(pomFilePath)
    const jarFile = findJarFile(subFiles)
    packageInfo.packing = jarFile ? 'jar' : 'pom'
    packageInfo.pomPath = pomFilePath
    if (jarFile) {
      packageInfo.jarPath = path.join(parentPath, jarFile)
    }
    packages.push(packageInfo)
  } else
    for (const subFile of subFiles) {
      const subFilePath = path.join(parentPath, subFile)
      const isDir = await futils.isDir(subFilePath)
      if (isDir) {
        await recursionfindPackageInfo(subFilePath, packages)
      }
    }
}

const findErrorLog = (logText) => {
  let errorText = ''
  const regex = /\[ERROR\] .*/g
  const matches = logText.matchAll(regex)
  for (const match of matches) {
    errorText += `${match[0]}\n`
  }
  return errorText
}


export default class MavenPackagesTruck {
  constructor() {}

  async packing(args, logCallback) {
    const settingsPath = await this.createPackingSettingsXml(args)
    const { isSuccess } = await mvn.install(
      {
        mavenHome: args.mavenHome,
        settingsPath,
        pomPath: args.pomPath
      },
      (data) => {
        if (logCallback) {
          logCallback(data)
        }
      }
    )
    return {
      isSuccess
    }
  }

  /**
   * 卸货
   * @param {*} params
   * @param {*} logCallback
   * @param {*} progressCallback
   * @param {{retry:number, concurrency:number}} options
   * @returns
   */
  async unpacking(params, logCallback, progressCallback, options) {
    logger.info('Delploy Start')
    const {retry, concurrency} =  {...uploadOptions, ...options}
    const args = { ...params, repositoryId: 'custom-mirror' }
    let packageInfoList = []
    logCallback("搜索包中...\n")
    await recursionfindPackageInfo(args.respositroyDir, packageInfoList)
    logCallback(`搜索完毕, 共计${packageInfoList.length}\n`)
    const progress = { totalCount: packageInfoList.length, doneCount: 0 }
    progressCallback(progress)

    const settingsPath = await this.createUnPackingSettingsXml(args)

    const pLimit =  plimit(concurrency);

    const downloadPromises = packageInfoList.map((item) => {
      let retryNum = 0
      const uploadFile = async (packageInfo) => {
        retryNum++
        const {isSuccess, logText} = await mvn.deployFile({
          ...packageInfo,
          settingsPath,
          mavenHome: args.mavenHome,
          repositoryId: args.repositoryId,
          repositoryUrl: args.repositoryUrl
        })


        let errorLogText = '';
        const content = packageLogContent(packageInfo)
        if(isSuccess){
          progress.doneCount++
          logCallback(`[upload success] (${progress.doneCount}/${progress.totalCount}) ${content} [${retryNum}]\n `)
        }else {
          if(retryNum < retry){
            console.log("retry:", retryNum)
            return uploadFile(packageInfo)
          }

          progress.doneCount++
          if(isErrorLogText(logText)){
            errorLogText = findErrorLog(logText)
          }else {
            errorLogText = logText
          }
          logCallback(`[upload error] (${progress.doneCount}/${progress.totalCount}) ${content} [${retryNum}] \n ${errorLogText}`)
        }

        progressCallback(progress)
        return { isSuccess, errorLogText, packageInfo }
      }
      return pLimit(async () => {
        return uploadFile(item)
      })
    })

    logCallback(`准备上传  重试次数: ${retry}; 并发数: ${concurrency}\n`)
    return Promise.all(downloadPromises)
      .then((values) => {
        const totalCount = progress.totalCount
        const errorList = values
          .filter((item) => !item.isSuccess)
          .map((item) => ({ packageInfo: item.packageInfo, logText: item.errorLogText }))
        return {
          totalCount,
          errorCount: errorList.length,
          errorList
        }
      })
      .catch((err) => {
        logCallback(err)
      })
  }

  //生成Install settings.xml
  async createPackingSettingsXml(args) {
    const xmlDir = cfg.getAppHome()
    const fileName = 'pack_settings.xml'
    const tplData = {
      localRepository: args.localRepository,
      mirrorUrl: args.mirrorUrl
    }
    const tplPath = path.join(__dirname, 'pack_settings.tpl')
    const content = template(tplPath, tplData)
    await futils.writeToFile(xmlDir, fileName, content)
    return path.join(xmlDir, fileName)
  }

  //生成deploy settings.xml
  async createUnPackingSettingsXml(args) {
    const xmlDir = cfg.getAppHome()
    const fileName = 'unpack_settings.xml'
    const tplData = {
      repositoryId: args.repositoryId,
      repositoryUrl: args.repositoryUrl,
      username: args.username,
      password: args.password
    }
    const tplPath = path.join(__dirname, 'unpack_settings.tpl')
    const content = template(tplPath, tplData)
    await futils.writeToFile(xmlDir, fileName, content)
    return path.join(xmlDir, fileName)
  }
}
